Ocular Cataract Intelligent Recognition (OCIR) ¶
Leveraging the power of Deep Learning, our trained Convolutional Neural Network(CNN) model aims to detect one of the most common ocular conditions- Cataract.
Project Team:
- Joshua S Raju 22BAI1213
- Shubham Yadav 22BAI1427
- Md Rameez Haider 22BRS1327
DISCLAIMER: While our CNN model aids in cataract detection, it is not a substitute for professional medical advice. Please consult a qualified healthcare provider for a comprehensive eye examination and diagnosis.
DATASET: https://www.kaggle.com/datasets/andrewmvd/ocular-disease-recognition-odir5k https://www.kaggle.com/datasets/jr2ngb/cataractdataset
Importing Dependencies¶
In [1]:
import numpy as np
import os
import cv2
import matplotlib.pylab as plt
import imghdr
import tensorflow as tf
Tensorflow GPU Management
In [2]:
gpus = tf.config.experimental.list_physical_devices('GPU')
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
Cleaning Image Data¶
In [3]:
data_dir = 'imgdata_v3'
Creating Image Dataset¶
In [4]:
dataset = tf.keras.utils.image_dataset_from_directory(data_dir, image_size=(256, 256))
Found 7437 files belonging to 2 classes.
In [5]:
batch = dataset.as_numpy_iterator().next()
len(batch)
Out[5]:
2
In [6]:
classes = ['cataract', 'normal']
classes
Out[6]:
['cataract', 'normal']
In [7]:
fig, axs = plt.subplots(5,5, figsize=(20,20))
for idx, img in enumerate(batch[0][:25]):
row = idx//5
col = idx%5
axs[row, col].imshow(img.astype(int))
axs[row, col].axis('off')
axs[row, col].set_title(classes[batch[1][idx]])
Scaling Dataset¶
In [8]:
batch[0][0].shape, batch[0][0].max(), batch[0][0].min()
Out[8]:
((256, 256, 3), 254.75, 0.0)
In [9]:
data = dataset.map(lambda x,y: (x/255, y))
In [10]:
print(data.as_numpy_iterator().next()[0][0].shape,
data.as_numpy_iterator().next()[0][0].max(),
data.as_numpy_iterator().next()[0][0].min())
(256, 256, 3) 1.0 0.0
Splitting Dataset¶
In [11]:
len(data)
Out[11]:
233
In [12]:
train_size = int(len(data)*0.7)
val_size = int(len(data)*0.2)+1
test_size = int(len(data)*0.1)
train_size + val_size + test_size
Out[12]:
233
In [13]:
train_data = data.take(train_size)
val_data = data.skip(train_size).take(val_size)
test_data = data.skip(train_size+val_size).take(test_size)
Training CNN Model¶
In [14]:
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.regularizers import l2
In [15]:
model = Sequential()
model.add(Conv2D(16, (3,3), 1, activation='relu', input_shape=(256,256,3)))
model.add(MaxPooling2D())
model.add(Dropout(0.4))
model.add(Conv2D(32, (3,3), 1, activation='relu'))
model.add(MaxPooling2D())
model.add(Dropout(0.5))
model.add(Conv2D(16, (3,3), 1, activation='relu'))
model.add(MaxPooling2D())
model.add(Dropout(0.3))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.3))
model.add(Dense(1, kernel_regularizer=l2(0.001), activation='linear'))
model.compile(optimizer='adam', loss='hinge', metrics='accuracy')
model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 254, 254, 16) 448
max_pooling2d (MaxPooling2D (None, 127, 127, 16) 0
)
dropout (Dropout) (None, 127, 127, 16) 0
conv2d_1 (Conv2D) (None, 125, 125, 32) 4640
max_pooling2d_1 (MaxPooling (None, 62, 62, 32) 0
2D)
dropout_1 (Dropout) (None, 62, 62, 32) 0
conv2d_2 (Conv2D) (None, 60, 60, 16) 4624
max_pooling2d_2 (MaxPooling (None, 30, 30, 16) 0
2D)
dropout_2 (Dropout) (None, 30, 30, 16) 0
flatten (Flatten) (None, 14400) 0
dense (Dense) (None, 256) 3686656
dropout_3 (Dropout) (None, 256) 0
dense_1 (Dense) (None, 1) 257
=================================================================
Total params: 3,696,625
Trainable params: 3,696,625
Non-trainable params: 0
_________________________________________________________________
In [16]:
logdir = 'LogDir'
try:
os.mkdir(logdir)
except Exception as e:
pass
tfb_callback = tf.keras.callbacks.TensorBoard(logdir)
In [17]:
model_fit = model.fit(train_data, epochs=30, shuffle=True, validation_data=val_data, callbacks=[tfb_callback])
Epoch 1/30 163/163 [==============================] - 13s 50ms/step - loss: 0.6927 - accuracy: 0.6919 - val_loss: 0.5327 - val_accuracy: 0.6257 Epoch 2/30 163/163 [==============================] - 8s 48ms/step - loss: 0.3438 - accuracy: 0.8507 - val_loss: 0.4775 - val_accuracy: 0.6915 Epoch 3/30 163/163 [==============================] - 8s 48ms/step - loss: 0.2835 - accuracy: 0.8765 - val_loss: 0.4175 - val_accuracy: 0.7759 Epoch 4/30 163/163 [==============================] - 8s 47ms/step - loss: 0.2550 - accuracy: 0.8878 - val_loss: 0.3359 - val_accuracy: 0.8145 Epoch 5/30 163/163 [==============================] - 8s 48ms/step - loss: 0.2673 - accuracy: 0.8892 - val_loss: 0.2837 - val_accuracy: 0.8690 Epoch 6/30 163/163 [==============================] - 8s 48ms/step - loss: 0.2396 - accuracy: 0.8969 - val_loss: 0.3367 - val_accuracy: 0.8344 Epoch 7/30 163/163 [==============================] - 8s 50ms/step - loss: 0.2333 - accuracy: 0.8984 - val_loss: 0.3092 - val_accuracy: 0.8890 Epoch 8/30 163/163 [==============================] - 9s 52ms/step - loss: 0.2239 - accuracy: 0.9016 - val_loss: 0.2911 - val_accuracy: 0.8816 Epoch 9/30 163/163 [==============================] - 9s 55ms/step - loss: 0.2101 - accuracy: 0.9103 - val_loss: 0.2712 - val_accuracy: 0.8896 Epoch 10/30 163/163 [==============================] - 14s 88ms/step - loss: 0.2069 - accuracy: 0.9112 - val_loss: 0.2333 - val_accuracy: 0.8923 Epoch 11/30 163/163 [==============================] - 15s 90ms/step - loss: 0.1890 - accuracy: 0.9206 - val_loss: 0.2094 - val_accuracy: 0.9156 Epoch 12/30 163/163 [==============================] - 15s 90ms/step - loss: 0.1913 - accuracy: 0.9233 - val_loss: 0.2221 - val_accuracy: 0.8963 Epoch 13/30 163/163 [==============================] - 15s 91ms/step - loss: 0.1901 - accuracy: 0.9195 - val_loss: 0.1773 - val_accuracy: 0.9315 Epoch 14/30 163/163 [==============================] - 14s 85ms/step - loss: 0.1777 - accuracy: 0.9243 - val_loss: 0.2022 - val_accuracy: 0.8996 Epoch 15/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1825 - accuracy: 0.9245 - val_loss: 0.2150 - val_accuracy: 0.8730 Epoch 16/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1667 - accuracy: 0.9291 - val_loss: 0.1533 - val_accuracy: 0.9242 Epoch 17/30 163/163 [==============================] - 8s 49ms/step - loss: 0.1585 - accuracy: 0.9321 - val_loss: 0.2060 - val_accuracy: 0.9289 Epoch 18/30 163/163 [==============================] - 8s 48ms/step - loss: 0.1579 - accuracy: 0.9323 - val_loss: 0.1934 - val_accuracy: 0.8896 Epoch 19/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1512 - accuracy: 0.9344 - val_loss: 0.1801 - val_accuracy: 0.9116 Epoch 20/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1443 - accuracy: 0.9388 - val_loss: 0.1515 - val_accuracy: 0.9435 Epoch 21/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1373 - accuracy: 0.9421 - val_loss: 0.1550 - val_accuracy: 0.9222 Epoch 22/30 163/163 [==============================] - 8s 48ms/step - loss: 0.1418 - accuracy: 0.9408 - val_loss: 0.1303 - val_accuracy: 0.9342 Epoch 23/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1148 - accuracy: 0.9513 - val_loss: 0.1598 - val_accuracy: 0.9262 Epoch 24/30 163/163 [==============================] - 8s 48ms/step - loss: 0.1183 - accuracy: 0.9538 - val_loss: 0.1371 - val_accuracy: 0.9368 Epoch 25/30 163/163 [==============================] - 8s 47ms/step - loss: 0.1135 - accuracy: 0.9519 - val_loss: 0.1417 - val_accuracy: 0.9242 Epoch 26/30 163/163 [==============================] - 8s 48ms/step - loss: 0.1145 - accuracy: 0.9513 - val_loss: 0.1525 - val_accuracy: 0.9069 Epoch 27/30 163/163 [==============================] - 8s 50ms/step - loss: 0.1160 - accuracy: 0.9498 - val_loss: 0.1439 - val_accuracy: 0.9355 Epoch 28/30 163/163 [==============================] - 8s 47ms/step - loss: 0.0992 - accuracy: 0.9588 - val_loss: 0.1409 - val_accuracy: 0.9235 Epoch 29/30 163/163 [==============================] - 8s 47ms/step - loss: 0.0973 - accuracy: 0.9590 - val_loss: 0.1334 - val_accuracy: 0.9275 Epoch 30/30 163/163 [==============================] - 8s 50ms/step - loss: 0.1002 - accuracy: 0.9586 - val_loss: 0.1911 - val_accuracy: 0.8870
Model Performance¶
In [18]:
fig, axs = plt.subplots(2,1, figsize=(8,8))
axs[0].plot(model_fit.history['loss'], color='red', label='Loss')
axs[0].plot(model_fit.history['val_loss'], color='orange', label='Val_Loss')
axs[0].legend(loc='upper right')
axs[1].plot(model_fit.history['accuracy'], color='green', label='Accuracy')
axs[1].plot(model_fit.history['val_accuracy'], color='teal', label='Val_Accuracy')
axs[1].legend(loc='upper left')
Out[18]:
<matplotlib.legend.Legend at 0x1d9b3307940>
Evaluating Model¶
In [19]:
from tensorflow.keras.metrics import Precision, Recall, BinaryAccuracy, SpecificityAtSensitivity
In [20]:
pre = Precision()
rec = Recall()
acc = BinaryAccuracy()
spe = SpecificityAtSensitivity(0.5)
In [21]:
for batch in test_data.as_numpy_iterator():
X, y = batch
y_pred = model.predict(X)
pre.update_state(y, y_pred)
rec.update_state(y, y_pred)
acc.update_state(y, y_pred)
spe.update_state(y, y_pred)
1/1 [==============================] - 0s 184ms/step 1/1 [==============================] - 0s 41ms/step 1/1 [==============================] - 0s 44ms/step 1/1 [==============================] - 0s 48ms/step 1/1 [==============================] - 0s 43ms/step 1/1 [==============================] - 0s 46ms/step 1/1 [==============================] - 0s 45ms/step 1/1 [==============================] - 0s 44ms/step 1/1 [==============================] - 0s 39ms/step 1/1 [==============================] - 0s 40ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 30ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 31ms/step 1/1 [==============================] - 0s 35ms/step 1/1 [==============================] - 0s 33ms/step 1/1 [==============================] - 0s 34ms/step 1/1 [==============================] - 0s 37ms/step 1/1 [==============================] - 0s 231ms/step
In [22]:
precision = pre.result().numpy()
recall = rec.result().numpy()
f1 = 2 * (precision*recall) / (precision+recall)
print('Accuracy\t:', acc.result().numpy())
print('Precision\t:', precision)
print('Recall\t\t:', recall)
print('Specificity\t:', spe.result().numpy())
print('F1\t\t:', f1)
Accuracy : 0.8926081 Precision : 0.98275864 Recall : 0.79831934 Specificity : 0.99444443 F1 : 0.8809892264552719
Exporting Model¶
In [23]:
model.save(os.path.join('models','ODIR_SVM.h5'))
In [24]:
from tensorflow.keras.models import load_model
In [25]:
new_model = load_model(os.path.join('models','ODIR_SVM.h5'))
new_model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 254, 254, 16) 448
max_pooling2d (MaxPooling2D (None, 127, 127, 16) 0
)
dropout (Dropout) (None, 127, 127, 16) 0
conv2d_1 (Conv2D) (None, 125, 125, 32) 4640
max_pooling2d_1 (MaxPooling (None, 62, 62, 32) 0
2D)
dropout_1 (Dropout) (None, 62, 62, 32) 0
conv2d_2 (Conv2D) (None, 60, 60, 16) 4624
max_pooling2d_2 (MaxPooling (None, 30, 30, 16) 0
2D)
dropout_2 (Dropout) (None, 30, 30, 16) 0
flatten (Flatten) (None, 14400) 0
dense (Dense) (None, 256) 3686656
dropout_3 (Dropout) (None, 256) 0
dense_1 (Dense) (None, 1) 257
=================================================================
Total params: 3,696,625
Trainable params: 3,696,625
Non-trainable params: 0
_________________________________________________________________